#pragma once

#ifdef _DEBUG
    #include <iostream>
    using std::wcout;
    using std::endl;
#endif

#include <windows.h>
#include <string>
#include <map>
#include <vector>
using std::wstring;
using std::string;
using std::map;
using std::vector;


namespace SubPlayer {
    extern const string InitFunctionName;
    extern const string GetErrorFunctionName;
    extern const string GetEndTimeFunctionName;
    extern const string GetFirstSubFunctionName;
    extern const string GetFirstSubSizeFunctionName;
    typedef LPCWSTR (*FN_SUPP_TYPE)(void);
    typedef int (*FN_INIT)(const LPCWSTR sometext, const LPCWSTR encoding);
    typedef LPCWSTR (*FN_GET_ERROR)(void);
    typedef long (*FN_GET_END_TIME)(void);
    typedef int (*FN_GET_FIRST_SUB_SIZE)(void);
    typedef errno_t (*FN_GET_FIRST_SUB)(long* endTime, LPWSTR sub, size_t maxStringSize);

    class PluginLoader
    {
    public:
        PluginLoader(const wstring &path);
        virtual ~PluginLoader();
        int LoadPlugin(const wstring &type);
        const vector<wstring> GetSupportedTypesList() const;
        int Initialize(const LPCWSTR subtitlePath, const LPCWSTR encoding) const;
        long GetEndTime() const;
        //inline wstring GetFirstSub() const {return 
        inline wstring GetError() const {return getErrorMessage();}
        errno_t GetFirstSub(wstring& sub, long & endTime) const;
    private:
        void LoadSupportedTypes(const wstring &path);
        wstring GetLibSupportedType(const wstring &path, HINSTANCE &dllInst) const;
        void FreeLibraries();
        static int DefaultInit(const LPCWSTR, const LPCWSTR);
        static LPCWSTR DefaultGetError();
        //inlinde LPCWSTR DefaultGetFirstSub(long & duration) {return 

        template<typename funcType>
        int GetFunction(HINSTANCE dllLibrary, funcType & func, const string & name)
        {
            if (dllLibrary != NULL) {
                func = (funcType) GetProcAddress(dllLibrary, name.c_str());
                if (func != (funcType) NULL) {
                    return 0; 
                }
                else {
                    return 2;
                }
            }
            else {
                return 1;
            }
        }

        map<wstring, HINSTANCE> supportedTypes;
        FN_INIT initFunction;
        FN_GET_ERROR getErrorMessage;
        FN_GET_END_TIME getEndTimeFunc;
        FN_GET_FIRST_SUB getFirstSubFunc;
        FN_GET_FIRST_SUB_SIZE getFirstSubSizeFunc;
    };
}